const path = require('path')

const overrideWebpackConfig = ({ context, webpackConfig, pluginOptions }) => {
  const {
    getLoader,
    loaderByName,
    throwUnexpectedConfigError,
  } = require('@craco/craco')

  // This is mocked in Windows tests
  const pathSep = module.exports.pathSep

  const throwError = (message, githubIssueQuery) =>
    throwUnexpectedConfigError({
      packageName: 'craco-less',
      githubRepo: 'DocSpring/craco-less',
      message,
      githubIssueQuery,
    })

  const lessExtension = /\.less$/
  const lessModuleExtension = /\.module\.less$/

  pluginOptions = pluginOptions || {}

  const oneOfRule = webpackConfig.module.rules.find((rule) => rule.oneOf)
  if (!oneOfRule) {
    throwError(
      "Can't find a 'oneOf' rule under module.rules in the " +
        `${context.env} webpack config!`,
      'webpack+rules+oneOf'
    )
  }

  const sassRule = oneOfRule.oneOf.find(
    (rule) => rule.test && rule.test.toString().includes('scss|sass')
  )
  const sassModuleRule = oneOfRule.oneOf.find(
    (rule) => rule.test && rule.test.toString().includes('module\\.(scss|sass')
  )
  if (!sassRule) {
    throwError(
      "Can't find the webpack rule to match scss/sass files in the " +
        `${context.env} webpack config!`,
      'webpack+rules+scss+sass'
    )
  }
  if (!sassModuleRule) {
    throwError(
      "Can't find the webpack rule to match scss/sass module files in the " +
        `${context.env} webpack config!`,
      'webpack+rules+scss+sass+module'
    )
  }

  const getUse = (sassLoaders, isModules) => {
    let use = []
    sassLoaders.forEach((ruleOrLoader) => {
      let rule
      if (typeof ruleOrLoader === 'string') {
        rule = {
          loader: ruleOrLoader,
          options: {},
        }
      } else {
        rule = ruleOrLoader
      }

      if (
        (context.env === 'development' || context.env === 'test') &&
        rule.loader.includes(`${pathSep}style-loader${pathSep}`)
      ) {
        use.push({
          loader: rule.loader,
          options: {
            ...rule.options,
            ...(pluginOptions.styleLoaderOptions || {}),
          },
        })
      } else if (rule.loader.includes(`${pathSep}css-loader${pathSep}`)) {
        use.push({
          loader: rule.loader,
          options: {
            ...rule.options,
            ...(pluginOptions.cssLoaderOptions || {}),
            modules: isModules
              ? pluginOptions.cssLoaderOptions.modules || false
              : false,
          },
        })
      } else if (rule.loader.includes(`${pathSep}postcss-loader${pathSep}`)) {
        use.push({
          loader: rule.loader,
          options: {
            ...rule.options,
            ...(pluginOptions.postcssLoaderOptions || {}),
          },
        })
      } else if (
        rule.loader.includes(`${pathSep}resolve-url-loader${pathSep}`)
      ) {
        use.push({
          loader: rule.loader,
          options: {
            ...rule.options,
            ...(pluginOptions.resolveUrlLoaderOptions || {}),
          },
        })
      } else if (
        context.env === 'production' &&
        rule.loader.includes(`${pathSep}mini-css-extract-plugin${pathSep}`)
      ) {
        use.push({
          loader: rule.loader,
          options: {
            ...rule.options,
            ...(pluginOptions.miniCssExtractPluginOptions || {}),
          },
        })
      } else if (rule.loader.includes(`${pathSep}sass-loader${pathSep}`)) {
        const defaultLessLoaderOptions =
          context.env === 'production' ? { sourceMap: true } : {}
        use.push({
          loader: require.resolve('less-loader'),
          options: {
            ...defaultLessLoaderOptions,
            ...pluginOptions.lessLoaderOptions,
          },
        })
      } else {
        throwError(
          `Found an unhandled loader in the ${context.env} webpack config: ${rule.loader}`,
          'webpack+unknown+rule'
        )
      }
    })
    return use
  }

  let lessRule = {
    exclude: lessModuleExtension,
    test: lessExtension,
    use: getUse(sassRule.use, false),
  }

  let lessModuleRule = {
    test: lessModuleExtension,
    use: getUse(sassModuleRule.use, true),
  }

  oneOfRule.oneOf.push(lessRule)
  oneOfRule.oneOf.push(lessModuleRule)

  const { isFound, match: fileLoaderMatch } = getLoader(
    webpackConfig,
    loaderByName('file-loader')
  )
  if (!isFound) {
    throwError(
      `Can't find file-loader in the ${context.env} webpack config!`,
      'webpack+file-loader'
    )
  }
  fileLoaderMatch.loader.exclude.push(lessExtension)

  return webpackConfig
}

// pathSep is mocked in Windows tests
module.exports = {
  overrideWebpackConfig,
  pathSep: path.sep,
}
